package crmdna.client.useractivity;

import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;

import java.util.Date;
import java.util.List;

import org.junit.After;
import org.junit.Before;
import org.junit.Test;

import com.google.appengine.tools.development.testing.LocalDatastoreServiceTestConfig;
import com.google.appengine.tools.development.testing.LocalServiceTestHelper;
import com.googlecode.objectify.ObjectifyFilter;

import crmdna.client.useractivity.UserActivityCore.UserActivityProp;
import crmdna.common.APIException;
import crmdna.common.APIResponse.Status;
import crmdna.crmdna.Client;

public class UserActivityTest {
	
	private final LocalServiceTestHelper datastoreHelper =
		        new LocalServiceTestHelper(new LocalDatastoreServiceTestConfig()
		        .setApplyAllHighRepJobPolicy());
	
	private final String client = "isha";
	private final String invalidClient = "invalid";	
	
    @Before
    public void setUp() {
        datastoreHelper.setUp();
        ObjectifyFilter.complete();
        
        Client.create(client);        
    }

    @After
    public void tearDown() {       
    	ObjectifyFilter.complete();
        datastoreHelper.tearDown();
    }
    
    @Test
    public void getUserActivityTest() throws InterruptedException {
    	//client has to be valid
    	final long userId = 1;
    	try {
    		UserActivityCore.getUserActivity(invalidClient, userId, 
    				null, null);
    		assertTrue(false);
    	} catch (APIException ex) {
    		assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
    	}
    	
    	//zero records before recording any activity
    	List<UserActivityProp> props = UserActivityCore.getUserActivity(client, 
    			userId, null, null);
    	assertEquals(0, props.size());
    	 
    	Date start = new Date();  
    	UserActivityCore.recordUserActivity(client, "GROUP", 3, 
    			"CREATE", null, userId + 2); //some other user id
    	UserActivityCore.recordUserActivity(client, "MEMBER", 3, 
    			"VIEW", null, userId);
    	int deltaMS = 100;
    	Thread.sleep(deltaMS);
    	Date startPlusDelta = new Date();
    	Thread.sleep(deltaMS);
    	UserActivityCore.recordUserActivity(client, "MEMBER", 4, 
    			"CREATE", "email: email@login.com", userId);
    	Thread.sleep(deltaMS);
    	Date startPlus3Delta = new Date();
    	UserActivityCore.recordUserActivity(client, "MEMBER", 4, 
    			"UPDATE", "email: [email@login.com] ---> [email2@login.com]", userId);
    	Thread.sleep(deltaMS);
    	
    	ObjectifyFilter.complete();
    	props = UserActivityCore.getUserActivity(client, userId, null, null);
    	
    	//do not specify start and end dates - should return all records
    	//sorted in descending order of timestamp
    	assertEquals(3, props.size());    	
    	assertEquals(userId, props.get(2).userId);
    	assertEquals("VIEW", props.get(2).userAction);
    	assertEquals(3, props.get(2).entityId);
    	assertEquals("MEMBER", props.get(2).entityType);
    	assertEquals(null, props.get(2).change);
    	
    	assertEquals(userId, props.get(1).userId);
    	assertEquals("CREATE", props.get(1).userAction);
    	assertEquals(4, props.get(1).entityId);
    	assertEquals("MEMBER", props.get(1).entityType);
    	assertEquals("email: email@login.com", props.get(1).change);
    	
    	assertEquals(userId, props.get(0).userId);
    	assertEquals("UPDATE", props.get(0).userAction);
    	assertEquals(4, props.get(0).entityId);
    	assertEquals("MEMBER", props.get(0).entityType);
    	assertEquals("email: [email@login.com] ---> [email2@login.com]", props.get(0).change);
    	
    	//specify start and end date
    	props = UserActivityCore.getUserActivity(client, userId, start, startPlusDelta);
    	assertEquals(1, props.size()); 
    	assertEquals(userId, props.get(0).userId);
    	assertEquals("VIEW", props.get(0).userAction);
    	assertEquals(3, props.get(0).entityId);
    	assertEquals("MEMBER", props.get(0).entityType);
    	assertEquals(null, props.get(0).change);
    	
    	//specify start but not end
    	props = UserActivityCore.getUserActivity(client, userId, startPlus3Delta, null);
    	assertEquals(1, props.size()); 
    	assertEquals(userId, props.get(0).userId);
    	assertEquals("UPDATE", props.get(0).userAction);
    	assertEquals(4, props.get(0).entityId);
    	assertEquals("MEMBER", props.get(0).entityType);
    	assertEquals("email: [email@login.com] ---> [email2@login.com]", props.get(0).change);
    	
    	//specify end but not start
    	props = UserActivityCore.getUserActivity(client, userId, null, startPlusDelta);
    	assertEquals(1, props.size()); 
    	assertEquals(userId, props.get(0).userId);
    	assertEquals("VIEW", props.get(0).userAction);
    	assertEquals(3, props.get(0).entityId);
    	assertEquals("MEMBER", props.get(0).entityType);
    	assertEquals(null, props.get(0).change);    	    	    	
    }
    
    @Test
    public void recordUserActivityTest() throws InterruptedException {
    	final long userId = 1;
    	//client has to be valid
    	try {
    		UserActivityCore.recordUserActivity(invalidClient, "MEMBER", 3, 
    				"VIEW", null, userId);
    		assertTrue(false);
    	} catch (APIException ex) {
    		assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
    	}
    	    
    	Date start = new Date();
    	Thread.sleep(100);
    	UserActivityCore.recordUserActivity(client, "MEMBER", 3, "VIEW", null, userId);
    	Thread.sleep(100);
    	Date end = new Date();
    	
    	ObjectifyFilter.complete();
    	List<UserActivityProp> props = UserActivityCore.getUserActivity(client, 
    			userId, null, null);
    	
    	assertEquals(1, props.size());
    	//System.out.println("props.get(0: " + new Gson().toJson(props.get(0)) + 
    	//		", start: " + start + ", end: " + end);
    	assertEquals("MEMBER", props.get(0).entityType);
    	assertEquals(3, props.get(0).entityId);
    	assertEquals(userId, props.get(0).userId);
    	assertTrue(props.get(0).timestamp.getTime() > start.getTime());
    	assertTrue(props.get(0).timestamp.getTime() < end.getTime());    	
    }
    
    @Test
    public void getEntityActivityTest() throws InterruptedException {
    	final long userId = 1;
    	
    	//client has to be valid
    	try {
    		UserActivityCore.getEntityActivity(invalidClient, "MEMBER", 3, null, null);
    		assertTrue(false);
    	} catch (APIException ex) {
    		assertEquals(Status.ERROR_RESOURCE_DOES_NOT_EXIST, ex.statusCode);
    	}
    	
    	//no records if non existing entity
    	List<UserActivityProp> userActivityProps = 
    			UserActivityCore.getEntityActivity(client, "MEMBER", 3, null, null);
    	assertEquals(0, userActivityProps.size());
    	    	
    	int deltaMS = 100;
    	UserActivityCore.recordUserActivity(client, "MEMBER", 2, "VIEW", null, userId);
    	Thread.sleep(deltaMS);
    	Date startPlusDelta = new Date();
    	Thread.sleep(deltaMS);
    	UserActivityCore.recordUserActivity(client, "MEMBER", 3, "UPDATE", "update 1", userId);
    	Thread.sleep(deltaMS);
    	Date startPlus3Delta = new Date();
    	UserActivityCore.recordUserActivity(client, "MEMBER", 3, "UPDATE", "update 2", userId);
    	Thread.sleep(100);
    	
    	//do not specify start and end
    	userActivityProps = UserActivityCore.getEntityActivity(client, "MEMBER", 3, null, null);
    	assertEquals(2, userActivityProps.size());
    	assertEquals("MEMBER", userActivityProps.get(0).entityType);
    	assertEquals(3, userActivityProps.get(0).entityId);
    	assertEquals(userId, userActivityProps.get(0).userId);
    	assertEquals("UPDATE", userActivityProps.get(0).userAction);
    	assertEquals("update 2", userActivityProps.get(0).change);
    	
    	assertEquals("MEMBER", userActivityProps.get(1).entityType);
    	assertEquals(3, userActivityProps.get(1).entityId);
    	assertEquals(userId, userActivityProps.get(1).userId);
    	assertEquals("UPDATE", userActivityProps.get(1).userAction);
    	assertEquals("update 1", userActivityProps.get(1).change);
    	
    	//specify only start date
    	userActivityProps = UserActivityCore.getEntityActivity(client, "MEMBER", 3, startPlus3Delta, null);
    	assertEquals(1, userActivityProps.size());
    	assertEquals("MEMBER", userActivityProps.get(0).entityType);
    	assertEquals(3, userActivityProps.get(0).entityId);
    	assertEquals(userId, userActivityProps.get(0).userId);
    	assertEquals("UPDATE", userActivityProps.get(0).userAction);
    	assertEquals("update 2", userActivityProps.get(0).change);
    	
    	//specify only end date
    	userActivityProps = UserActivityCore.getEntityActivity(client, "MEMBER", 3, null, startPlusDelta);
    	assertEquals(0, userActivityProps.size());    	    	
    }
}
